mod mna;
mod solve;

use std::collections::HashMap;
use mna::IValue;
use mna::MNAData;
use mna::NodeIndex;
use mna::VValue;
use mna::I;
use mna::R;
use mna::V;
use solve::Solver;

use crate::netlist::Instance;
use crate::netlist::Netlist;
use crate::netlist::Statement;

pub struct Simulator {
    netlist: Netlist,

    node_mapping: HashMap<String, NodeIndex>,
    probe_nodes: Vec<(String, usize)>,
    
}

impl Simulator {
    pub fn load_netlist(netlist: Netlist) -> Result<Self, String> {
        let node_mapping = netlist.node_maping();
        let node_mapping: HashMap<String, NodeIndex> = node_mapping.into_iter()
            .map(|(name, index)| -> (String, NodeIndex) {
                if index == 0 {
                    (name, NodeIndex::GND)
                } else {
                    (name, NodeIndex::Node(index - 1))
                }
            })
            .collect();

        let probe_nodes = netlist.probe_nodes();
        let mut probe_name_index_nodes = Vec::new();

        for node in probe_nodes {
            match node_mapping.get(&node) {
                Some(index) => match index {
                    NodeIndex::GND => return Err(format!("probe for gnd???")),
                    NodeIndex::Node(index) => probe_name_index_nodes.push((node, *index)),
                }
                None => return Err(format!("Un define node '{}'", node))
            }
        }

        Ok(Self{
            netlist, node_mapping, probe_nodes: probe_name_index_nodes
        })
    }

    pub fn simulate(&self, sim_time: f64, delta_time: f64) -> Result<HashMap::<String, Vec<f64>>, String> {
        let mut results = HashMap::<String, Vec<f64>>::new();
        for (node_name, _) in self.probe_nodes.iter() {
            results.insert(node_name.clone(), Vec::new());
        } 

        // Frist simulate
        let mut mnadata: MNAData = self.get_mnadata();
        
        // Loop
        while !mnadata.reach(sim_time) {
            let (x, y) = mnadata.build_mna();
            let result = Solver::Nalgebra.solve(x, y);

            // Update Result
            for (name, index) in self.probe_nodes.iter() {
                let res = *result.get(*index).unwrap();
                results.get_mut(name).unwrap().push(res);
            }

            // Update mna
            mnadata.update(delta_time, &result);
        }

        Ok(results)
    }

    fn get_mnadata(&self) -> MNAData {
        let mut rs = Vec::new();
        let mut vs = Vec::new();
        let mut is = Vec::new();

        for statement in self.netlist.statements.iter() {
            match statement {
                Statement::Instance(inst) => {
                    match inst {
                        Instance::R(r) => {
                            let r = R(
                                self.node_mapping.get(&r.node1).unwrap().clone(),
                                self.node_mapping.get(&r.node2).unwrap().clone(),
                                r.value
                            );
                            rs.push(r);
                        }
                        Instance::L(l) => {
                            let i = I(
                                self.node_mapping.get(&l.node1).unwrap().clone(),
                                self.node_mapping.get(&l.node2).unwrap().clone(),
                                IValue::Ind{ cur_i: 0., lnd: l.value }
                            );
                            is.push(i);
                        }
                        Instance::C(c) => {
                            let v = V(
                                self.node_mapping.get(&c.node1).unwrap().clone(),
                                self.node_mapping.get(&c.node2).unwrap().clone(),
                                VValue::Cap{ cur_v: 0., cap: c.value }
                            );
                            vs.push(v);
                        }
                        Instance::V(vol) => {
                            let v = V(
                                self.node_mapping.get(&vol.node1).unwrap().clone(),
                                self.node_mapping.get(&vol.node2).unwrap().clone(),
                                VValue::Src(vol.kind.clone())
                            );
                            vs.push(v);
                        }
                        Instance::I(cur) => {
                            let i = I(
                                self.node_mapping.get(&cur.node1).unwrap().clone(),
                                self.node_mapping.get(&cur.node2).unwrap().clone(),
                                IValue::Src(cur.value)
                            );
                            is.push(i);
                        }
                    }
                }
                Statement::Command(_) => {}
            }
        }

        MNAData {
            rs, vs, is, node_size: self.node_mapping.len() - 1, time: 0.
        }
    }

}

#[allow(unused)]
mod test {
    use super::*;

    #[test]
    fn test_load_from_file() {
        let simulator = 
            Simulator::load_netlist(
                Netlist::load_from_file("./benchmark/RLC_s3.sp.expand.sp").unwrap()
            ).unwrap();
        
        for (node_name, index) in simulator.node_mapping {
            println!("{} : {:?}", node_name, index);
        }

        for node in simulator.probe_nodes {
            println!("{:?}", node);
        }
    }
}